package app

import (
	"fmt"
	"net/http"
	"strconv"
	"encoding/json"
	"log"
	"math/rand"
	"time"
	"gopkg.in/gomail.v2"
	"gitee.com/linker-group/lsas/modules"
)

type Global struct {
	GlobalConfig     map[string]string //全局配置文件
	CookieExpiration int               //cookie过期时间

	UrlName         string
	UrlSuperiorMenu string //URL上级菜单ID
	UrlId           string //URLID
	UrlInstruction  string
	MenuInfo        []map[string]string
	MenuInfoAll     []map[string]string
	Info            map[string]string
	PageTitle       string
	modules.DB
	modules.Redis
	modules.UserInfo
	modules.Config
	modules.CCC
	modules.Ldap
	modules.Salt
	modules.Svn
}

type Return_Info struct {
	Status   string
	Hostinfo []map[string]string
}

type UserInfoUnmarshal struct {
	UserName  string `json:"UserName"`
	Passwd    string `json:"Passwd"`
	AuthToken string `json:"AuthToken"`
	Role      string    `json:"Role"`
	Prems []struct {
		MenuID string `json:"menu_id"`
	} `json:"Prems"`
}

type viewFunc func(http.ResponseWriter, *http.Request)

func (G *Global) AuthHandler(f viewFunc) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		//获取cookie过期时间
		if CookieExpiration, strconv_error := strconv.Atoi(G.GlobalConfig["cookie_expiration"]); strconv_error == nil {
			G.CookieExpiration = CookieExpiration
		} else {
			fmt.Fprintln(w, "redis_cookie过期时间设置必须为数字类型；配置文件：cookie_expiration")
			return
		}

		//获取MYsql配置
		G.DB.Config = G.Config.MysqlConfig()
		//初始化mysql连接
		if err := G.DB.Connect(); err != nil {
			fmt.Fprintf(w, "初始化mysql连接失败，请检查配置文件")
			return
		}
		//关闭mysql连接
		defer G.DB.Conn.Close() //关闭连接
		//获取saltstack配置
		G.Salt.Config = G.Config.SaltConfig()
		//获取svn配置
		G.Svn.Config = G.Config.SvnConfig()
		//获取redis配置
		G.Redis.Config = G.Config.RedisConfig()
		//初始化redis连接
		G.Redis.Connect()
		//关闭redis连接
		defer G.Redis.Conn.Close()

		//获取cookie信息
		cookie_username, cookie_username_err := r.Cookie("UserName")
		cookie_token, cookie_token_err := r.Cookie("AuthToken")

		//判断是否获取成功；获取失败重定向到登录页面

		current_url_info, Err := G.SelectOne("SELECT * FROM menu WHERE menu_url = ?", r.URL.Path);
		if Err != nil {
			log.Println("系统错误:" + Err.Error())
			fmt.Fprintln(w, "系统错误:"+Err.Error())
			return
		}

		if cookie_username_err != nil || cookie_token_err != nil {
			http.Redirect(w, r, "/login", http.StatusFound)
		} else {

			G.UserInfo.UserName = cookie_username.Value
			if UserInfo_json, err := G.Redis.Get(G.UserInfo.UserName); err == nil { //读取用户redis信息
				var JsonRes UserInfoUnmarshal
				json.Unmarshal([]byte(UserInfo_json), &JsonRes) //解析json
				//查询当前访问菜单信息

				//判断cookie token 与 redis token 是否一致
				if cookie_token.Value == JsonRes.AuthToken {
					//刷新Cookie 过期时间
					G.Redis.ExPire(G.UserInfo.UserName, G.CookieExpiration)
					//查询角色对应菜单地址
					if urlInfo, err := G.SelectAll("SELECT * FROM menu WHERE (id in (SELECT menu_id FROM permissions WHERE role_id = ?) AND menu_grade=2)OR menu_grade=1", JsonRes.Role); err == nil {
						G.MenuInfo = *urlInfo
					} else {
						fmt.Fprintln(w, "系统错误:"+err.Error())
						return
					}

					// 查询所有菜单地址
					if urlInfo, err := G.SelectAll("SELECT * FROM menu"); err == nil { //菜单地址
						G.MenuInfoAll = *urlInfo
					} else {
						fmt.Fprintln(w, "系统错误:"+err.Error())
						return
					}

					//首页不需要进行权限验证
					if r.URL.Path == "/" {
						G.UrlName = "仪表盘"
						G.UrlSuperiorMenu = ""
						G.UrlId = ""
						f(w, r)
						return
					}

					//权限验证模块
					if current_url_info["menu_url"] == r.URL.Path { //判断查询数据是否为空
						G.UrlId = current_url_info["id"]                      //urlID
						G.UrlName = current_url_info["menu_name"]             //URL名称
						G.UrlSuperiorMenu = current_url_info["superior_menu"] //URL上级菜单
						G.UrlInstruction = current_url_info["instruction"]    //URL说明

						if current_url_info["menu_grade"] == "3" { //对3级菜单的处理
							for _, k := range JsonRes.Prems {
								if k.MenuID == current_url_info["superior_menu"] { //redis中存在
									f(w, r)
									return
								}
							}

							fmt.Fprint(w, Get_Json("权限拒绝!"))
						} else if current_url_info["menu_grade"] == "2" { //对2级菜单的处理
							for _, k := range JsonRes.Prems {
								if k.MenuID == current_url_info["id"] {
									f(w, r)
									return
								}
							}
							G.Info = make(map[string]string)
							G.Info["url"] = "/"
							G.Info["message"] = "无权限访问此页面"
							http.Redirect(w, r, "/error", http.StatusFound)
						}
					} else {
							fmt.Fprint(w, Get_Json("访问菜单未查询到!"))
					}
				} else {
					if current_url_info["menu_grade"] == "3" {
							fmt.Fprint(w, Get_Json("账户异地登录，请重新登录!"))
					} else if current_url_info["menu_grade"] == "2" {
						G.Info = make(map[string]string)
						G.Info["url"] = "/login"
						G.Info["message"] = "该账户异地登录，被迫下线，请重新登录!"
						http.Redirect(w, r, "/error", http.StatusFound)
					}
				}
			} else {
				if current_url_info["menu_grade"] == "3" {
						fmt.Fprint(w, Get_Json("会话超时，请重新登录!"))
				} else if current_url_info["menu_grade"] == "2" {
					G.Info = make(map[string]string)
					G.Info["url"] = "/login"
					G.Info["message"] = "会话超时，请重新登录!"
					http.Redirect(w, r, "/error", http.StatusFound)
				} else {
					http.Redirect(w, r, "/login", http.StatusFound)
				}
			}
		}
	}
}

func Get_Json(status string) (string) {
	var return_info_json Return_Info
	return_info_json.Status = status
	if _json, Err := json.Marshal(return_info_json); Err == nil {
		return string(_json)
	} else {
		log.Println(Err)
		return ""
	}
}

func (G *Global) Handler(f viewFunc) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		//获取cookie过期时间
		if cookie_expiration, strconv_error := strconv.Atoi(G.GlobalConfig["cookie_expiration"]); strconv_error == nil {
			G.CookieExpiration = cookie_expiration
		} else {
			fmt.Fprintln(w, "redis_cookie过期时间设置必须为数字类型；配置文件：cookie_expiration")
			return
		}

		G.DB.Config = G.Config.MysqlConfig() //初始化mysql配置文件
		if err := G.DB.Connect(); err != nil { //初始化mysql连接
			fmt.Fprintf(w, "初始化mysql连接失败，请检查配置文件")
			return
		}
		defer G.DB.Conn.Close()

		G.Salt.Config = G.Config.SaltConfig()
		G.Redis.Config = G.Config.RedisConfig()
		G.Redis.Connect() //初始化redis连接
		defer G.Redis.Conn.Close()
		//初始化连接结束

		f(w, r)

	}
}
func (G *Global) RandStr(num int) string {
	codes := "123456789abcdefghjkmnpqrstuvwxyzABCDEFGHKMNPQRSTUVWXYZ"
	codeLen := len(codes)
	data := make([]byte, num)
	rand.Seed(time.Now().UnixNano())

	for i := 0; i < num; i++ {
		idx := rand.Intn(codeLen)
		data[i] = byte(codes[idx])
	}
	return string(data)
}

//返回当前时间戳
func (G *Global) GetUnixNano() string {
	cur := time.Now()
	a := cur.UnixNano() / 1000000000
	return strconv.FormatInt(a, 10)
}

//发送邮件函数
func (G *Global) EMAIL(address, Subject, body string) error {
	m := gomail.NewMessage()
	m.SetAddressHeader("From", G.GlobalConfig["mail_user"], "") // 发件人
	m.SetHeader("To", // 收件人
		m.FormatAddress(address, ""),
	)
	m.SetHeader("Subject", Subject)
	m.SetBody("text/html", body)
	main_port, _ := strconv.Atoi(G.GlobalConfig["mail_port"])
	d := gomail.NewPlainDialer(G.GlobalConfig["mail_host"], main_port, G.GlobalConfig["mail_user"], G.GlobalConfig["mail_passwd"])
	if err := d.DialAndSend(m); err != nil {
		return err
	}
	return nil
}

//判断数组中包含某个值
func (G *Global) CheckStrings(array[]string,value string)  bool {
	for _,i:= range array{
		if i== value {
			return true
		}
	}
	return false
}